const fs = require('fs');
const path = require('path');
const fsWalk = require('@nodelib/fs.walk');
const util = require('util');
const crypto = require('crypto');

const fileUtils = {
  /**
   * 获取文件 stat
   * @param filePath
   * @returns
   */
  async getFileStat(filePath) {
    return fs.promises.stat(filePath);
  },
  /**
   * 获取文件 slice 方法
   * @param paths
   */
  async fileSlice(filePath, start, end) {
    const fileHandle = await fs.promises.open(filePath, 'r');
    const bufferArray = new Uint8Array(end - start);
    const { buffer } = await fileHandle.read(
      bufferArray,
      0,
      end - start,
      start
    );
    fileHandle.close();
    return buffer;
  },
  /**
   * 使用 node 计算 sha1
   * @param filePath
   * @param stopFlag
   * @returns
   */
  async calcSha1(filePath, preSize, stopFlag) {
    let options;
    if (preSize) {
      options = {
        start: 0,
        end: preSize - 1,
        highWaterMark: Math.min(64 * 1024, preSize - 1),
      };
    }
    const stream = fs.createReadStream(filePath, options);
    const hash = crypto.createHash('sha1');
    for await (const chunk of stream) {
      if (stopFlag && stopFlag()) {
        throw new Error('stop');
      }
      hash.update(chunk);
    }
    const sha1 = hash.digest('hex');
    return sha1;
  },
  /**
   * 读取所有文件
   * @param paths
   */
  async readPaths(filePaths, walkDirectory = false) {
    const walk = util.promisify(fsWalk.walk);
    const settings = new fsWalk.Settings({
      stats: true,
      followSymbolicLinks: true,
      entryFilter: (entry) => {
        if (entry.name === '.DS_Store') {
          return false;
        }
        return true;
      },
    });
    const promises = filePaths.map((filePath) => {
      return fs.promises.stat(filePath).then((stats) => {
        if (stats.isDirectory() && walkDirectory) {
          return walk(filePath, settings).then((entries) => {
            return {
              name: path.basename(filePath),
              path: filePath.split(path.sep).join('/'),
              stats,
              entries: entries.map((entry) => ({
                ...entry,
                path: entry.path.split(path.sep).join('/'),
              })),
              isDirectory: stats.isDirectory(),
            };
          });
        } else {
          return {
            name: path.basename(filePath),
            path: filePath.split(path.sep).join('/'),
            stats,
            isDirectory: stats.isDirectory(),
          };
        }
      });
    });
    return await Promise.all(promises);
  },
  parsePath(filePath) {
    return path.parse(filePath);
  },
};

module.exports = fileUtils;
